package com.rou.im.framework.boot.starter.builder;

import com.rou.im.framework.boot.starter.AbstractConfigurer;
import com.rou.im.framework.boot.starter.configurer.ExceptionProcessorConfigurer;
import com.rou.im.framework.boot.starter.configurer.RequestContextPersistenceProcessorConfigurer;
import com.rou.im.framework.boot.starter.postprocessor.ObjectPostProcessor;
import com.rou.im.framework.boot.starter.postprocessor.ProcessorStackConfigurerAdapter;
import com.rou.im.framework.core.processorstack.Processor;
import com.rou.im.framework.core.processorstack.ProcessorStack;
import com.rou.im.framework.core.processorstack.TargetAction;
import com.rou.im.framework.core.processorstack.impl.DefaultProcessorStack;

import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * <p></p>
 *
 * @author GaoShuangWen
 * @since 2020-05-05 16:42
 */
public class ProcessorStackBuilder extends AbstractProcessorStackConfigureBuilder<ProcessorStackBuilder> implements ProcessorStackConfigureBuilder<ProcessorStackBuilder> {

    private String id;

    private List<Processor> processors = new CopyOnWriteArrayList<>();

    private ProcessorComparator comparator = new ProcessorComparator();

    private TargetAction action;

    ProcessorStackConfigurerAdapter processorStackConfigurerAdapter;


   /* public ProcessorStackBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
        super(objectPostProcessor);
    }*/
    public ProcessorStackBuilder(ObjectPostProcessor<Object> objectPostProcessor,ProcessorStackConfigurerAdapter processorStackConfigurerAdapter) {
        super(objectPostProcessor);
        this.processorStackConfigurerAdapter = processorStackConfigurerAdapter;
    }

    public ProcessorStackBuilder id(String id) {
        this.id = id;
        return this;
    }
    public ExceptionProcessorConfigurer exception() throws Exception {
       return getOrApply(new ExceptionProcessorConfigurer());
    }
    public ProcessorStackBuilder action(TargetAction action) throws Exception {
        this.action = action;
        return this;
    }

    @Override
    protected ProcessorStack performBuild() {
        processors.sort(comparator);
        return new DefaultProcessorStack(processors,action);
    }

    @Override
    protected void beforeInit() throws Exception {
        // 初始化
        add(new ExceptionProcessorConfigurer());
        add(new RequestContextPersistenceProcessorConfigurer());
    }
    protected void beforeConfigure() throws Exception {
        processorStackConfigurerAdapter.configure(this);
    }
    @Override
    public ProcessorStackBuilder addProcessor(Processor processor) {
        Class<? extends Processor> pClass = processor.getClass();
        if (!comparator.isRegistered(pClass)) {
            throw new IllegalArgumentException(
                    "The Processor class "
                            + pClass.getName()
                            + " does not have a registered order and cannot be added without a specified order");
        }

        this.processors.add(processor) ;
        return this;
    }
    @Override
    public ProcessorStackBuilder addProcessorAfter(Processor processor, Class<Processor> afClass) {
        comparator.registerAfter(processor.getClass(),afClass);
        this.processors.add(processor);
        return this;
    }
    @Override
    public ProcessorStackBuilder addProcessorBefore(Processor processor, Class<Processor> bfClass) {
        comparator.registerBefore(processor.getClass(),bfClass);
        this.processors.add(processor);
        return this;
    }
   @Override
   public ProcessorStackBuilder addProcessorAt(Processor processor, Class<Processor> atClass) {
        comparator.registerAt(processor.getClass(),atClass);
        this.processors.add(processor);
        return this;
    }

    @Override
    public ProcessorStackBuilder remove(Class<Processor> atClass) {
        Iterator<Processor> iterator = processors.iterator();
        while (iterator.hasNext()){
            if(iterator.next().getClass().isInstance(atClass)){
                iterator.remove();
                break;
            }
        }
        return this;
    }
    public <C extends AbstractConfigurer<ProcessorStackBuilder>> C apply(C configurer)
            throws Exception {
        configurer.addObjectPostProcessor(getObjectPostProcessor());
        configurer.setBuilder(this);
        this.add(configurer);
        return configurer;
    }

    private <C extends AbstractConfigurer<ProcessorStackBuilder>> C getOrApply(
            C configurer) throws Exception {
        C existingConfig = (C) getConfigurer(configurer.getClass());
        if (existingConfig != null) {
            return existingConfig;
        }
        return apply(configurer);
    }



}
